#ifndef CUDA_DRIVER_API_LAUNCHER_H
#define CUDA_DRIVER_API_LAUNCHER_H


#include <windows.h>
#include <GL/gl.h>
#include <cuda.h>
#include <cudaGL.h>

//#include <hash_map>
#include <unordered_map>
#include <vector>
#include <string>

#pragma comment(lib,"cuda.lib")

//define grid or block dimension
struct vec3i
{
	vec3i(int _x,int _y,int _z){x = _x; y = _y; z = _z;}
	int x, y, z;	  
};
struct vec2i
{
	vec2i(int _x,int _y){x = _x; y = _y; }
	int x, y;
};

//stl hash map func object
struct eqstr
{
	bool operator() (const char* s1, const char* s2) const
	{
		return strcmp(s1, s2) == 0;
	}
};

//typedef stdext::hash_map<const char*, int> HashMap_str_int;
//TR1 standard hash_map
typedef std::tr1::unordered_map<const char*, int> UnorderedMap_str_int;

class CudaDriverAPILauncher
{
public:
	CudaDriverAPILauncher();
	~CudaDriverAPILauncher();

	bool InitDriver();
	bool InitDriverGL();

	//load kernel
	bool LoadCudaModule(const void* cubin);
	bool LoadCudaModule(const char* cubinFile);
	CUfunction getKernelFunction(const char* kernelFunc);

	//set kernel parameters
	int setParami(CUfunction kernel, int &offset, int value);
	int setParamf(CUfunction kernel, int &offset, float value);
	int setParamv(CUfunction kernel, int &offset, CUdeviceptr value);
	int setParamPtr(CUfunction kernel, int &offset, CUdeviceptr value);

	//accesser 
	CUmodule      getModule();
    CUtexref         getTexRef           (const char* name);
    //void              setTexRef           (const char* name, Buffer& buf, CUarray_format format, int numComponents);
    void                setTexRef           (const char* name, CUdeviceptr ptr, long size, CUarray_format format, int numComponents);
    void                setTexRef           (const char* name, CUarray cudaArray, bool wrap = true, bool bilinear = true, bool normalizedCoords = true, bool readAsInt = false);
    void                unsetTexRef         (const char* name);
    void                updateTexRefs       (CUfunction kernel);

	//cuda Texture
	//type is determined by the elementSize for now
	//create cuArrayBind to texture
	bool createCUArrayFromMemory(const char* arrayName, void* dataPointer, int x, int y, int z, int numChannel, CUarray_format format, CUevent_flags_enum = CU_EVENT_DEFAULT );
	bool createCUDATextureFromMemory(const char* texName, void* , int x, int y, int z, int elementSize);

	//static calls
	static CUarray createCUArrayFromMemory();

		

	//launch kernel
	void LaunchKernel(CUfunction kernel, int paramSize, const vec3i& blockSize, const vec2i& gridSize, 
									bool async=false, CUstream stream=0);
	float LaunchKernelTimed();

	bool checkError(CUresult res);
	bool checkError(const char* funcName, CUresult res);

	//static function



private:
	bool init();
	const char* decodeError(CUresult res);

private:

    static bool  _inited;
    static bool  _available;

	CUdevice  _cuDevice;
	CUcontext _cuContext;
	
	CUmodule _cuModule;
	CUfunction _cuKernelFunc;

	//std::vec<Buffer*>      m_globals;
    //Hash<String, S32>   m_globalHash;
	int _deviceCount;
   
	//Array<Buffer*>      m_globals;   
	//HashMap_str_int _globalHash;

	UnorderedMap_str_int _texRefHash;
	std::vector<CUtexref> _texRefs;

};

#endif